package com.course.redis.service;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.RandomUtil;
import com.course.common.enums.RedisKeyConstant;
import com.course.redis.mapper.UserPointsMapper;
import com.course.redis.model.pojo.UserPoints;
import com.google.common.collect.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.*;

/**
 * @PROJECT_NAME: course
 * @DESCRIPTION: 批量生成数据
 * @Author: 涂玄武
 * @DATE: 2021/7/9 15:14
 */
@Service
public class BatchDataService {

    private static final Logger log = LoggerFactory.getLogger(BatchDataService.class);

    @Resource
    private UserPointsMapper userPointsMapper;

    @Resource
    private RedisTemplate redisTemplate;

    private static final Snowflake SNOWFLAKE = new Snowflake(2, 3);

    //核心线程数
    private static final Integer CorePoolSize = 4;

    //队列中可以容纳/等待被处理的任务数量的阙值的设定
    private static final BlockingQueue BLOCKING_QUEUE = new ArrayBlockingQueue(30);

    //超过了能处理任务的最大线程数的任务的处理策略
    private static final ThreadPoolExecutor.CallerRunsPolicy POLICY = new ThreadPoolExecutor.CallerRunsPolicy();

    //核心线程数;最大线程数;当线程数>核心线程数时那些空闲线程在关闭之前等待新任务到来的最大等待时间;任务队列;拒绝策略
    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(CorePoolSize, 8, 10000, TimeUnit.SECONDS,
            BLOCKING_QUEUE, POLICY);

    /**
     * 批量预生成并插入数据-多线程
     *
     * @param userTotal
     * @param pointsCount
     * @throws Exception
     */
    public void batchInitItem(Integer userTotal, Integer pointsCount) throws Exception {
        List<BatchCallable> list = Lists.newLinkedList();
        for (int i = 1; i <= userTotal; i++) {
            list.add(new BatchCallable(i, pointsCount));
        }
        EXECUTOR.invokeAll(list);
    }

    class BatchCallable implements Callable<Boolean> {

        private Integer userId;

        private Integer total;

        public BatchCallable(Integer userId, Integer total) {
            this.userId = userId;
            this.total = total;
        }

        /**
         * 真正的实现批量插入数据的代码逻辑
         *
         * @return
         * @throws Exception
         */
        @Override
        public Boolean call() throws Exception {
            try {
                List<UserPoints> items = Lists.newLinkedList();
                Integer i = 0;
                for (; i < total; i++) {
                    Integer points = RandomUtil.randomInt(1,100);
                    items.add(new UserPoints(userId, points, 0));
                    //将积分保存到Redis中
                    redisTemplate.opsForZSet().incrementScore(
                            RedisKeyConstant.user_points.getKey(),userId,points);
                }
                userPointsMapper.insertBatch(items);
            } catch (Exception e) {
                log.error("异常：", e);
            }
            return true;
        }
    }

}
